#include "Random.h"
#include <time.h>

/* Period parameters */  		
#define CMATH_M 397
#define CMATH_MATRIX_A 0x9908b0df   /* constant vector a */
#define CMATH_UPPER_MASK 0x80000000 /* most significant w-r bits */
#define CMATH_LOWER_MASK 0x7fffffff /* least significant r bits */

/* Tempering parameters */   
#define CMATH_TEMPERING_MASK_B 0x9d2c5680
#define CMATH_TEMPERING_MASK_C 0xefc60000
#define CMATH_TEMPERING_SHIFT_U(y)  (y >> 11)
#define CMATH_TEMPERING_SHIFT_S(y)  (y << 7)
#define CMATH_TEMPERING_SHIFT_T(y)  (y << 15)
#define CMATH_TEMPERING_SHIFT_L(y)  (y >> 18)

#define MAXUINT     ( (unsigned int)~((unsigned int)0) )
#define MAXINT      ( (int)( MAXUINT >> 1 ) )

Random::Random()
{
	Initialize( true );
}

void Random::SetRandomSeed( unsigned int n )
{
	/* setting initial seeds to m_mt[N] using         */
	/* the generator Line 25 of Table 1 in          */
	/* [KNUTH 1981, The Art of Computer Programming */
	/*    Vol. 2 (2nd Ed.), pp102]                  */

	m_mt[0] = n & 0xffffffff;
	for ( m_mti = 1; m_mti < CMATH_N; ++m_mti )
	{
		m_mt[ m_mti ] = ( 69069 * m_mt[ m_mti - 1 ] ) & 0xffffffff;
	}

	m_rSeed = n;
}

void Random::Randomize( void )
{
	SetRandomSeed( (unsigned int)time( NULL ) );
}

void Random::Initialize( bool isRandomize )
{
	m_rSeed		= 1;
	m_rSeedSp	= 0;
	m_mti		= CMATH_N + 1;

	if ( isRandomize )
	{
		Randomize();
	}
}

unsigned int Random::GetInt( unsigned int max )
{
	unsigned long y;
	static unsigned long mag01[ 2 ]= { 0x0, CMATH_MATRIX_A };

	if ( max == 0 )
	{
		return 0;
	}

	if ( m_mti >= CMATH_N ) 
	{ 
		/* generate N words at one time */
		int kk;

		/* if sgenrand() has not been called, */
		if ( m_mti == CMATH_N + 1 )   
		{
			/* a default initial seed is used  */
			SetRandomSeed( 4357 ); 
		}

		for ( kk = 0; kk < CMATH_N - CMATH_M; ++kk ) 
		{
			y = ( m_mt[ kk ] & CMATH_UPPER_MASK ) | ( m_mt[ kk + 1 ] & CMATH_LOWER_MASK );
			m_mt[ kk ] = m_mt[ kk + CMATH_M ] ^ ( y >> 1 ) ^ mag01[y & 0x1 ];
		}

		for ( ; kk < CMATH_N - 1; ++kk ) 
		{
			y = ( m_mt[ kk ] & CMATH_UPPER_MASK ) | ( m_mt[ kk + 1 ] & CMATH_LOWER_MASK );
			m_mt[ kk ] = m_mt[ kk + ( CMATH_M - CMATH_N ) ] ^ ( y >> 1 ) ^ mag01[ y & 0x1 ];
		}

		y = ( m_mt[ CMATH_N - 1 ] & CMATH_UPPER_MASK ) | ( m_mt[ 0 ] & CMATH_LOWER_MASK );
		m_mt[ CMATH_N - 1 ] = m_mt[ CMATH_M - 1 ] ^ ( y >> 1 ) ^ mag01[ y & 0x1 ];

		m_mti = 0;
	}
	  
	y = m_mt[ m_mti++ ];
	y ^= CMATH_TEMPERING_SHIFT_U( y );
	y ^= CMATH_TEMPERING_SHIFT_S( y ) & CMATH_TEMPERING_MASK_B;
	y ^= CMATH_TEMPERING_SHIFT_T( y ) & CMATH_TEMPERING_MASK_C;
	y ^= CMATH_TEMPERING_SHIFT_L( y );

	return ( y % max );
}

unsigned int Random::GetInt(unsigned int min, unsigned int max){
	return min + GetInt(max - min);
}

float Random::GetFloat( void )
{
	float r = (float)GetInt( MAXINT );
	float divisor = (float)MAXINT;
	return ( r / divisor );
}

float Random::GetFloat(float max)
{
    return ( GetFloat() * max );
}

float Random::GetFloat(float min, float max)
{
    return min + ( max - min ) * GetFloat();
}

